package com.biodiv.transport.factory;

import com.alibaba.fastjson2.JSONObject;
import com.biodiv.client.loadBalan.BlockingLoadBalancerClient;
import com.biodiv.client.loadBalan.LoadBalancerClient;
import com.biodiv.common.instance.ServiceInstance;
import com.biodiv.miracle.ioc.context.ApplicationContext;
import com.biodiv.miracle.web.support.RequestMethod;
import com.biodiv.miracle.web.support.annotation.RequestBody;
import com.biodiv.miracle.web.support.annotation.RequestMapping;
import com.biodiv.miracle.web.support.annotation.RequestParam;
import com.biodiv.transport.support.TransportClient;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: tianquan
 * @date: 2025-05-30  10:08
 * @description: Transport 代理类，用于动态生成远程调用逻辑。
 */
public class TransportProxy implements InvocationHandler {

    // 内部类 TransportBoost 负责具体的请求处理逻辑
    private final TransportBoost transportBoost = new TransportBoost();

    // 缓存 Object 类中的所有方法名（如 toString、equals 等），避免被代理调用
    private static final List<String> CLASS_LIST = Arrays.stream(Object.class.getMethods()).map(Method::getName).toList();

    // 全局复用的 HttpClient 实例
    private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().build();

    // IOC 容器上下文，用于获取 Bean（如负载均衡器）
    private final ApplicationContext applicationContext;

    // 当前代理的目标接口类
    private final Class<?> clazz;

    // 默认协议头
    private static final String HTTP = "http://";

    // 接口级别的基础路径（来自 @RequestMapping 注解）
    private String baseUrl = "";

    // TransportClient 注解信息，包含服务名或 URL
    private final TransportClient transportClient;


    /**
     * 构造函数
     *
     * @param clazz           被代理的接口类
     * @param applicationContext IOC 容器上下文
     */
    public TransportProxy(Class<?> clazz, ApplicationContext applicationContext) {
        this.clazz = clazz;
        this.applicationContext = applicationContext;
        // 获取接口上的 TransportClient 注解
        transportClient = clazz.getAnnotation(TransportClient.class);
        // 获取接口上的 RequestMapping 注解，提取 base path
        RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
        if (requestMapping != null) {
            baseUrl = requestMapping.value();
        }
    }

    /**
     * 动态代理的入口方法，当接口方法被调用时会进入此方法
     *
     * @param proxy  代理对象
     * @param method 被调用的方法
     * @param args   方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return transportBoost.invoke(method, args);
    }

    public class TransportBoost {
        public Object invoke(Method method, Object[] args) throws Throwable {
            // 忽略 Object 类的方法（如 toString、hashCode）
            if (CLASS_LIST.contains(method.getName())) {
                return this.toString();
            }

            // 获取方法上的 RequestMapping 注解
            RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
            if (requestMapping == null) {
                throw new IllegalArgumentException("方法" + method.getName() + "没有RequestMapping注解");
            }

            // 提取请求方法类型（GET/POST 等）
            RequestMethod requestMethod = requestMapping.method();

            // 拼接最终请求 URL
            String requestUrl = getRequestUrl(requestMapping);

            // 解析 URL 参数（@RequestParam）
            Map<String, String> urlParams = parseUrlParams(method, args);

            // 解析请求体参数（@RequestBody）
            Map<String, Object> bodyParams = parseRequestBody(requestMethod, args, method);

            // 发送 HTTP 请求并获取响应
            HttpResponse<String> response = sendRequest(requestMethod, requestUrl, urlParams, bodyParams);

            // 解析响应并返回结果
            return parseResponseBody(response, method);
        }

        /**
         * 解析方法中带有 @RequestParam 注解的参数，作为 URL 查询参数
         *
         * @param method 被调用的方法
         * @param args   方法参数值
         * @return 包含 URL 参数的 Map
         */
        private Map<String, String> parseUrlParams(Method method, Object[] args) {
            Parameter[] parameters = method.getParameters();
            Map<String, String> urlParams = new HashMap<>();
            for (int i = 0; i < parameters.length; i++) {
                RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
                if (requestParam == null) {
                    continue;
                }
                String paramName = requestParam.value();
                String paramValue = args[i] == null ? null : String.valueOf(args[i]);
                urlParams.put(paramName, paramValue);
            }
            return urlParams;
        }

        /**
         * 解析方法中带有 @RequestBody 注解的参数，构建请求体
         *
         * @param requestMethod 请求方法类型（GET/POST）
         * @param args          方法参数值
         * @param method        被调用的方法
         * @return 包含请求体内容的 Map
         */
        private Map<String, Object> parseRequestBody(RequestMethod requestMethod, Object[] args, Method method) throws IllegalAccessException {
            Map<String, Object> bodyParams = new HashMap<>();

            Parameter[] parameters = method.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                RequestBody requestBody = parameter.getAnnotation(RequestBody.class);
                if (requestBody == null) {
                    continue;
                }

                // 支持基本类型和字符串直接作为 body 参数
                if (parameter.getType().isPrimitive() || parameter.getType().isAssignableFrom(String.class)) {
                    bodyParams.put(parameter.getName(), args[i]);
                    continue;
                }

                // 处理复杂对象：反射获取属性并填充到 bodyParams 中
                if (!requestMethod.equals(RequestMethod.GET)) {
                    Object arg = args[i];
                    if (arg == null) {
                        continue;
                    }
                    Class<?> typeClass = parameter.getType();
                    Field[] fields = typeClass.getDeclaredFields();
                    for (Field field : fields) {
                        field.setAccessible(true);
                        String name = field.getName();
                        Object value = field.get(arg);
                        bodyParams.put(name, value);
                    }
                }
            }
            return bodyParams;
        }

        /**
         * 构建带有查询参数的 URL 字符串
         *
         * @param baseUrl 基础 URL
         * @param params  URL 参数
         * @return 完整的 URL 字符串
         */
        private String buildUrlWithParams(String baseUrl, Map<String, String> params) {
            if (params.isEmpty()) {
                return baseUrl;
            }
            StringBuilder sb = new StringBuilder(baseUrl).append("?");
            for (Map.Entry<String, String> entry : params.entrySet()) {
                sb.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
                        .append("=")
                        .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
                        .append("&");
            }
            return sb.deleteCharAt(sb.length() - 1).toString();
        }


        private Object parseResponseBody(HttpResponse<String> response, Method method) {
            if (response == null) {
                return null;
            }
            String body = response.body();
            if (body == null || body.isEmpty()) {
                return null;
            }
            if ("404 Not Found".equals(body)) {
                throw new RuntimeException("404 Not Found");
            }
            Class<?> returnType = method.getReturnType();
            return JSONObject.parseObject(body, returnType);
        }

        /**
         * 根据 TransportClient 和 RequestMapping 注解构建完整的请求 URL
         *
         * @param requestMapping 方法上的 RequestMapping 注解
         * @return 完整的请求 URL
         */
        private String getRequestUrl(RequestMapping requestMapping) {
            // 用户直接指定了访问的uri，拼接请求路径
            if (!transportClient.url().isEmpty()) {
                String requestUrl = transportClient.url() + baseUrl + requestMapping.path();
                if (!requestUrl.startsWith("http")) {
                    return HTTP + requestUrl;
                }
            }

            // 指定了服务名，去找对应的服务地址
            String serviceName = transportClient.value();
            if (serviceName != null && !serviceName.isEmpty()) {
                String serviceAddress = getServerAddress(serviceName);
                return HTTP + serviceAddress + baseUrl + requestMapping.path();
            }

            return null;
        }

        /**
         * 发送 HTTP 请求并获取响应
         *
         * @param requestMethod 请求方法（GET/POST）
         * @param requestUrl    请求地址
         * @param urlParams     URL 参数
         * @param bodyParams    请求体参数
         * @return HTTP 响应
         */
        private HttpResponse<String> sendRequest(RequestMethod requestMethod, String requestUrl, Map<String, String> urlParams, Map<String, Object> bodyParams) {
            try {
                // 拼接url参数
                requestUrl = buildUrlWithParams(requestUrl, urlParams);

                // 构建请求体
                HttpRequest.BodyPublisher bodyPublisher = bodyParams.isEmpty() ?
                        HttpRequest.BodyPublishers.noBody() :
                        HttpRequest.BodyPublishers.ofString(JSONObject.toJSONString(bodyParams));

                // 构建 HttpRequest 请求
                HttpRequest request = HttpRequest.newBuilder()
                        .header("Content-Type", "application/json")
                        .method(requestMethod.name(), bodyPublisher)
                        .uri(URI.create(requestUrl))
                        .build();

                // 发送请求并获取响应
                return HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }

    /**
     * 获取指定服务名对应的服务实例地址（通过负载均衡器）
     *
     * @param serverName 服务名
     * @return 服务实例地址（host:port）
     */
    private String getServerAddress(String serverName) {
        // 获取负载均衡器
        LoadBalancerClient loadBalancerClient = applicationContext.getBean(LoadBalancerClient.class);
        if (loadBalancerClient == null) {
            // 没有配置就用默认的负载均衡器
            loadBalancerClient = new BlockingLoadBalancerClient();
        }
        ServiceInstance serviceInstance = loadBalancerClient.choose(serverName);
        return serviceInstance.getHost() + ":" + serviceInstance.getPort();
    }


}
